#!/usr/bin/python
#
# debfile.py
#
# A pure Python way of producing .deb files suitable installation
# on the Maemo platform.
#
#
# Notes:
#
#  * Intended for use with companion `distutils` `bdist_maemo` command but
#    may also be able to be used standalone.
#
#  * May _also_ be used as a generic .deb packager but it's not
#    been tested for that.
#
#
#  Author: follower@rancidbacon.com
#
#    Date: 15 September 2006
#
# License: GPL 2.0
#
#

import os
import stat
import time
import tarfile
from tarfile import TarFile as _TarFile

from cStringIO import StringIO

import arfile

FILENAME_DEB_VERSION = "debian-binary"
FILE_CONTENT_DEB_VERSION = "2.0\x0a"

FILENAME_CONTROL_TAR_GZ = "control.tar.gz"
FILENAME_DATA_TAR_GZ = "data.tar.gz"

PERMS_URW_GRW_OR = stat.S_IRUSR | stat.S_IWUSR | \
                   stat.S_IRGRP | stat.S_IWGRP | \
                   stat.S_IROTH

UID_ROOT = 0
GID_ROOT = 0

class ControlFile(object):
    """
    """

    def __init__(self,
                 long_description = "",
                 Description = "",
                 **kwargs):
        """
        """
        self.options = kwargs # TODO: Is order important?

        # TODO: Clean-up special handling of description
        self.description = Description
        self.long_description = long_description
        


    def _getContent(self):
        """
        """
        content = ["%s: %s" % (k, v)
                   for k,v in self.options.iteritems()]

        if self.description:
            content.append("Description: %s" % self.description)
            if self.long_description:
                content.append(" " + self.long_description)

        return "\n".join(content) + "\n"

    content = property(_getContent, doc="")


class TarFile(_TarFile):
    """
    """

    def addfilefromstring(self, name, theString):
        """
        """
        content = StringIO(theString)

        theFileInfo = tarfile.TarInfo(name = name)
        theFileInfo.mtime = int(time.time()) # Absence seems to break tgz file.
        theFileInfo.size = len(content.getvalue())

        self.addfile(theFileInfo, fileobj = content)        


class MaemoPackage(object):
    """
    """

    def __init__(self, controlFile, dataDirectoryPath):
        """
        """
        self.controlFile = controlFile
        self._dataDirectoryPath = dataDirectoryPath


    def packed(self):
        """
        """
        theDeb = arfile.ArFile()

        ## Add the debian package version
        theDeb.files.append(self._getVersionFile())
        
        ## Add the compressed control related file(s)
        theDeb.files.append(self._getControlFiles())

        ## Add compressed data file(s)
        theDeb.files.append(self._getDataFiles())

        return theDeb.packed()
        

    def _getVersionFile(self):
        """
        """
        debVersionFile = \
                arfile.FileInfo(name = FILENAME_DEB_VERSION,
                                modificationTime = int(time.time()),
                                userId = UID_ROOT,
                                groupId = GID_ROOT,
                                fileMode = PERMS_URW_GRW_OR,
                                fileSize = len(FILE_CONTENT_DEB_VERSION),
                                data = FILE_CONTENT_DEB_VERSION)

        return debVersionFile


    def _getControlFiles(self):
        """
        """
        debControlFile = self.controlFile.content

        outputFileObj = StringIO() # TODO: Do more transparently?

        tarOutput = TarFile.open(FILENAME_CONTROL_TAR_GZ,
                                 mode = "w:gz",
                                 fileobj = outputFileObj)

        tarOutput.addfilefromstring("control", debControlFile)

        # TODO: Add `postinst` here if needed.

        tarOutput.close()

        control_tar_gz = outputFileObj.getvalue()

        controlFile = arfile.FileInfo(name = FILENAME_CONTROL_TAR_GZ,
                                      modificationTime = int(time.time()),
                                      userId = UID_ROOT,
                                      groupId = GID_ROOT,
                                      fileMode = PERMS_URW_GRW_OR,
                                      fileSize = len(control_tar_gz),
                                      data = control_tar_gz)

        return controlFile


    def _getDataFiles(self):
        """
        """
        directoryPath = self._dataDirectoryPath

        outputFileObj = StringIO() # TODO: Do more transparently?

        tarOutput = TarFile.open(FILENAME_DATA_TAR_GZ,
                                 mode = "w:gz",
                                 fileobj = outputFileObj)

        # Note: We can't use this because we need to fiddle permissions:
        #       tarOutput.add(directoryPath, arcname = "")

        # TODO: Add this as a method for TarFile and tidy-up?
        for root, dirs, files in os.walk(directoryPath):
            archiveRoot = root[len(directoryPath):]

            tarinfo = tarOutput.gettarinfo(root, archiveRoot)
            # TODO: Make configurable?
            tarinfo.uid = UID_ROOT
            tarinfo.gid = GID_ROOT
            tarinfo.uname = ""
            tarinfo.gname = ""
            tarOutput.addfile(tarinfo)

            for f in  files:
                tarinfo = tarOutput.gettarinfo(os.path.join(root, f),
                                               os.path.join(archiveRoot, f))
                tarinfo.uid = UID_ROOT
                tarinfo.gid = GID_ROOT
                tarinfo.uname = ""
                tarinfo.gname = ""
                
                # Fix up file permissions
                suggested_perm = tarinfo.mode
                ## remove all WRITE perms (possibly this should be done in bdist_maemo)
                suggested_perm = suggested_perm & ~stat.S_IWUSR & ~stat.S_IWGRP & ~stat.S_IWOTH
                if tarinfo.name.startswith('usr/bin/'):
                    # Tack on EXECUTE perms, some file systems do not have EXECUTE permissions (e.g. FAT)
                    suggested_perm = suggested_perm | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH ## S_IEXEC  doesn't do what I expect under win32 not sure about linux
                tarinfo.mode = suggested_perm
                
                tarOutput.addfile(tarinfo, file(os.path.join(root, f)))

        tarOutput.close()

        data_tar_gz = outputFileObj.getvalue()

        dataFile = arfile.FileInfo(name = FILENAME_DATA_TAR_GZ,
                                   modificationTime = int(time.time()),
                                   userId = UID_ROOT,
                                   groupId = GID_ROOT,
                                   fileMode = PERMS_URW_GRW_OR,
                                   fileSize = len(data_tar_gz),
                                   data = data_tar_gz)
        return dataFile
            
            


if __name__ == "__main__":
    # debian-binary
    # control.tgz
    # data.tgz

    import sys

    try:
        directoryPath = sys.argv[1]
    except IndexError:
        print "Usage: %s <directory path>" % sys.argv[0]
        raise SystemExit

    theMaemoPackage = MaemoPackage(
        ControlFile(Package="xx-example",
                    Version="0.1.0",
                    Section="user/other",
                    Priority="optional",
                    Architecture="all",
                    Maintainer="follower <follower@rancidbacon.com>",
                    Depends="python2.4, python2.4-hildon, python2.4-gtk2",
                    Description="Example packaged Maemo application."
                    ),
        directoryPath
        )

    sys.stdout.write(theMaemoPackage.packed())

    
                                     

    

